using System;
using Neo.Core;

namespace Neo.Framework
{
    ///<summary>Generic Wrapper around the FactoryMethods
    ///</summary>
    ///<typeparam name="T">An IEntityObject</typeparam>
    public abstract class GenericObjectFactory<T> where T : IEntityObject
    {
        #region Fields

        private IObjectContext _context;
        private IEntityMap _entityMap;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="GenericObjectFactory&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="aContext">A context.</param>
        /// <param name="objectType">Type of the object.</param>
        public GenericObjectFactory(IObjectContext aContext, Type objectType)
        {
            _context = aContext;
            _entityMap = Context.EntityMapFactory.GetMap(objectType);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the current ObjectContext.
        /// </summary>
        /// <value>The context.</value>
        public IObjectContext Context
        {
            get { return _context; }
        }

        /// <summary>
        /// Gets the EntityMap for this EntityObject.
        /// </summary>
        /// <value>The entity map.</value>
        public IEntityMap EntityMap
        {
            get { return _entityMap; }
        }

        #endregion

        #region Abstract Methods

        /// <summary>
        /// Finds the first.
        /// </summary>
        /// <param name="qualifierFormat">The qualifier format.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public abstract T FindFirst(string qualifierFormat, params object[] parameters);

        /// <summary>
        /// Finds the first.
        /// </summary>
        /// <param name="q">The q.</param>
        /// <returns></returns>
        public abstract T FindFirst(Qualifier q);

        #endregion

        #region Protected Methods

        /// <summary>
        /// Creates the object.
        /// </summary>
        /// <param name="pkvalues">The pkvalues.</param>
        /// <returns></returns>
        protected T CreateObject(object[] pkvalues)
        {
            return (T) Context.CreateObject(EntityMap.ObjectType, pkvalues);
        }

        /// <summary>
        /// Finds the object.
        /// </summary>
        /// <param name="pkvalues">The pkvalues.</param>
        /// <returns></returns>
        protected T FindObject(object[] pkvalues)
        {
            return (T) Context.GetObjectFromTable(EntityMap.TableName, pkvalues);
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Finds all objects.
        /// </summary>
        /// <returns></returns>
        public ObjectList<T> FindAllObjects()
        {
            return new ObjectList<T>(Context.GetObjects(new FetchSpecification(EntityMap)));
        }

        /// <summary>
        /// Finds the specified fetch spec.
        /// </summary>
        /// <param name="fetchSpec">The fetch spec.</param>
        /// <returns></returns>
        public ObjectList<T> Find(IFetchSpecification fetchSpec)
        {
            if ((fetchSpec.EntityMap == null) && (fetchSpec is FetchSpecification))
                ((FetchSpecification) fetchSpec).EntityMap = EntityMap;
            return new ObjectList<T>(Context.GetObjects(fetchSpec));
        }

        /// <summary>
        /// Finds the specified qualifier.
        /// </summary>
        /// <param name="qualifier">The qualifier.</param>
        /// <returns></returns>
        public ObjectList<T> Find(Qualifier qualifier)
        {
            FetchSpecification f = new FetchSpecification(EntityMap, qualifier);
            return Find(f);
        }

        /// <summary>
        /// Finds the specified qualifier format.
        /// </summary>
        /// <param name="qualifierFormat">The qualifier format.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public ObjectList<T> Find(string qualifierFormat, params object[] parameters)
        {
            Qualifier q = Qualifier.Format(qualifierFormat, parameters);
            return Find(q);
        }

        /// <summary>
        /// Finds the with limit.
        /// </summary>
        /// <param name="limit">The limit.</param>
        /// <param name="qualifierFormat">The qualifier format.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public ObjectList<T> FindWithLimit(int limit, string qualifierFormat, params object[] parameters)
        {
            Qualifier q = Qualifier.Format(qualifierFormat, parameters);
            FetchSpecification f = new FetchSpecification(EntityMap, q, limit);
            return Find(f);
        }

        /// <summary>
        /// Finds the unique.
        /// </summary>
        /// <param name="qualifierFormat">The qualifier format.</param>
        /// <param name="parameters">The parameters.</param>
        /// <returns></returns>
        public T FindUnique(string qualifierFormat, params object[] parameters)
        {
            ObjectList<T> results = FindWithLimit(2, qualifierFormat, parameters);

            if (results.Count != 1)
                throw new NotUniqueException(results.Count > 1, qualifierFormat, parameters);

            return results[0];
        }

        #endregion
    }
}